//
//  main.m
//  OC_07_内存管理
//
//  Created by wyx on 16/3/3.
//  Copyright © 2016年 Apon. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "Person.h"
#pragma mark - Xcode如何查看内存内容
void viewMemory() {
    //int a = 100;
    int b = 300;
    int *p = NULL;
    p = &b;
    NSLog(@"over");
}

#pragma mark - 知识点1: 对象及对象的实例变量在堆中的地址
void chapter1() {
    
    /**
     * 定义Person类: 实例变量可见度为 @public
     * 详见 Person类 文件
     */
    
#if 0
    /** 创建对象Person1, 并赋值 */
    Person *person1 = [[Person alloc] init];
    person1->_name = @"zhangsan";
    person1->_sex = @"male";
    person1->_age = 18;
    
    /* 直接显示 对象大小 . 需要导入<objc/runtime.h> 文件 */
    NSLog(@"size:%ld", class_getInstanceSize([person1 class]));
    
    NSLog(@"************************************************");
    NSLog(@"---%d---(person1)对象和对象的实例变量在堆中的地址---", __LINE__);
    NSLog(@"person1      add:%p", person1);
    NSLog(@"person1 name add:%p", &person1->_name);
    NSLog(@"person1 age  add:%p", &person1->_age);
    NSLog(@"person1 sex  add:%p", &person1->_sex);
    
    NSLog(@"***********************************************");
    NSLog(@"---如果实例变量类型是对象类型, 指针内容是一个指针---");
    NSLog(@"---%d---(person1)实例变量的指针指向的地址---", __LINE__);
    NSLog(@"person1 name contant :%p", person1->_name);
    NSLog(@"person1 sex  contant :%p", person1->_sex);
    NSLog(@"字符串(zhangsan)   add:%p", @"zhangsan");
    NSLog(@"字符串(male)       add:%p", @"male");
    /*!^ 结论
     * 实例变量的指针最终指向了字符串常量首地址
     */
    
    NSLog(@"---如果实例变量类型是非对象类型, 指针内容即为值---");
    NSLog(@"person1 age  contant :%p", person1->_age);
    NSLog(@"person1 age  contant :%ld", person1->_age);
    
    
    /** 创建另一个对象Person2 */
    Person *person2 = [[Person alloc] init];
    person2 = person1;
    
    NSLog(@"************************************************");
    NSLog(@"---%d---(person2)对象和对象的实例变量在堆中的地址---", __LINE__);
    NSLog(@"person2      add:%p", person2);
    NSLog(@"person2 name add:%p", &person2->_name);
    NSLog(@"person2 age  add:%p", &person2->_age);
    NSLog(@"person2 sex  add:%p", &person2->_sex);
    /*!^ 结论
     * person2 指向了person1的首地址
     * person2 的实例变量地址也指向person1 的实例变量首地址
     */
    
    NSLog(@"************************************************");
    NSLog(@"---%d---(person2)实例变量的指针指向的地址---", __LINE__);
    NSLog(@"person2 name contant :%p", person2->_name);
    NSLog(@"person2 sex  contant :%p", person2->_sex);
    NSLog(@"字符串(zhangsan)   add:%p", @"zhangsan");
    NSLog(@"字符串(male)       add:%p", @"male");
    
    
    /* 使用copy方法创建person3 对象 */
    Person *person3 = [person1 copy];
    
    NSLog(@"************************************************");
    NSLog(@"---%d---(person3)对象和对象的实例变量在堆中的地址---", __LINE__);
    NSLog(@"person3      add:%p", person3);
    NSLog(@"person3 name add:%p", &person3->_name);
    NSLog(@"person3 age  add:%p", &person3->_age);
    NSLog(@"person3 sex  add:%p", &person3->_sex);
    
    
    NSLog(@"************************************************");
    NSLog(@"---%d---(person3)实例变量的指针指向的地址---", __LINE__);
    NSLog(@"person3 name contant :%p", person3->_name);
    NSLog(@"person3 sex  contant :%p", person3->_sex);
    NSLog(@"字符串(zhangsan)   add:%p", @"zhangsan");
    NSLog(@"字符串(male)       add:%p", @"male");
    
#endif
    
}

#pragma mark - 知识点2: 内存管理
void chapter2() {
        /** 内存管理方式:垃圾回收机制, MRC(人工引用计数),ARC(自动引用计数)
         OS X 开发,用垃圾回收机制, iOS 开发: MAC ARC
         */
}

#pragma mark - 知识点3: 影响引用计数的方法
void chapter3() {
#if 0
    /** alloc 方法. */
    Person *per = [[Person alloc] init];
    NSInteger count = [per retainCount];
    NSLog(@"%ld",count);
    /** retain 方法.+1 */
    [per retain];//2
    NSLog(@"%ld", [per retainCount]);
    [per retain];//3
    NSLog(@"%ld", [per retainCount]);
    /** release 方法. */
    [per release];//2
    NSLog(@"%ld", [per retainCount]);
    [per release];//1
    NSLog(@"%ld", [per retainCount]);
    [per release];//还是1
    NSLog(@"%ld", [per retainCount]);

    /** autorelease 方法. */
    Person *per = [[Person alloc] init];
    NSLog(@"%ld", [per retainCount]);
    [per retain];
    [per autorelease];//此时并没有减一,,是在未来某个时刻减一
    NSLog(@"%ld",[per retainCount]);

    /** autoreleasespool 自动释放池. */
    Person *per  = nil;
    Person *per1 = [[Person alloc] init];
    @autoreleasepool {
        //自动释放池内部
        per = [[Person alloc] init];
        [per retain];
        [per retain];
        [per autorelease];
        NSLog(@"%ld",[per retainCount]);
        [per1 retain];
    }
    NSLog(@"%ld",[per retainCount]);
    NSLog(@"%ld",[per1 retainCount]);//是被带用过的发送 release 消息
#endif
    /** dealloc 方法. */
    //系统自己调用,引用计数为0
    Person *per1 = [[Person alloc] init];
    per1.name = @"zhangsan";
    NSLog(@"%@",per1.name);
    [per1 release];//此时对象自动调用 dealloc 方法,释放内存,为了观察 dealloc 过程,重写 dealloc方法
}

#pragma mark - 知识点4: copy方法
void chapter4() {
    Person * per = [[Person alloc] init];
    per.name =@"zhangsan";
    //创建对象副本
    //Person *per1 = [per copy];
    
    
}

#pragma mark - 知识点5: 属性的内部实现原理
void chapter5() {
        /** 详见 Person 类的 setter 和 getter 方法 */
    
}

#pragma mark - 知识点6: dealloc内释放实例变量
void chapter6() {
    /** 详见 Person 类 */
    
    
}

#pragma mark - 知识点7: 便利构造器方法的实现原理
void chapter7() {
    /** 详见 Person 的构造器方法 */
    //注意使用构造器方法创建对象,后面不能 release, 否则过度释放
    Person *per = [Person personWithName:@"Tom" age:@"20" gen:@"m"];
    //coding
    //错误[per release];
    NSLog(@"%@",per);
}

#pragma mark - 知识点8: collection 的内存管理
void chapter8() {
    /** 容器自动管理 里面元素对象的引用计数
        对象加入到容器中,引用计数会自动+1
        对象从容器中移除,引用计数会自动-1
        容器对象销毁,在销毁前对里面所有对象发送 release 消息(即引用计数-1)
     */
    
}
#pragma mark - MRC 下一个自定义类的常用写法
void chapter9(){
    /** 详见. Film 类 */
}
int main(int argc, const char * argv[]) {
    //viewMemory();
    chapter3();
    chapter4();
    return 0;
}
